package com.opennetexam.manager.utils;

import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.*;
import net.lingala.zip4j.util.Zip4jConstants;
import org.springframework.util.StringUtils;

import java.io.*;
import java.lang.reflect.Array;
import java.util.*;
/**
 * 奶茶
 * Created on 2017/11/19.
 */
public class ZipUtils {

    /**
     * 使用给定密码解压指定的ZIP压缩文件到指定目录
     * <p>
     * 如果指定目录不存在,可以自动创建,不合法的路径将导致异常被抛出
     * @param zip 指定的ZIP压缩文件
     * @param dest 解压目录
     * @param passwd ZIP文件的密码
     * @return 解压后文件数组 + 压缩文件注释信息
     * @throws ZipException 压缩文件有损坏或者解压缩失败抛出
     */
    public static Map<String, Object> unzip(String zip, String dest, String passwd) throws Exception {
        File zipFile = new File(zip);
        return unzip(zipFile, dest, passwd);
    }

    /**
     * 使用给定密码解压指定的ZIP压缩文件到当前目录
     * @param zip 指定的ZIP压缩文件
     * @param passwd ZIP文件的密码
     * @return  解压后文件数组 + 压缩文件注释信息
     * @throws ZipException 压缩文件有损坏或者解压缩失败抛出
     */
    public static Map<String, Object> unzip(String zip, String passwd) throws Exception {
        File zipFile = new File(zip);
        File parentDir = zipFile.getParentFile();
        return unzip(zipFile, parentDir.getAbsolutePath(), passwd);
    }

    /**
     * 使用给定密码解压指定的ZIP压缩文件到指定目录
     * <p>
     * 如果指定目录不存在,可以自动创建,不合法的路径将导致异常被抛出
     * @param dest 解压目录
     * @param passwd ZIP文件的密码
     * @return  解压后文件数组 + 压缩文件注释信息
     * @throws ZipException 压缩文件有损坏或者解压缩失败抛出
     */
    public static Map<String, Object> unzip(File zipFile, String dest, String passwd) throws Exception {
        Map<String,Object> returnData = new HashMap<>();
        ZipFile zFile = new ZipFile(zipFile);

        zFile.setFileNameCharset("GBK");
        if (!zFile.isValidZipFile()) {
            throw new ZipException("压缩文件不合法,可能被损坏.");
        }
        File destDir = new File(dest);
        if (destDir.isDirectory() && !destDir.exists()) {
            destDir.mkdir();
        }
        //获取压缩文件注释信息 获取解压密码
        String[] strings = zFile.getComment().replace("\r", "").split("\n");
        if(strings.length > 0){
            for (int i=0;i<strings.length;i++){
                String[] split = strings[i].split("=");
                if(split.length > 1){
                    if(("sn").equals(split[0])){
                        //获取加密字符串 并解密;
                        returnData.put("packageKey",strings[i].substring(3));
                        passwd = EncrptUtil.examPlanPagkageDESDecrypt(strings[i].substring(3));
                    }
                }
            }
        }

        if(passwd == null || ("").equals(passwd)){
            returnData.put("errorInfo","请检查系统版本是否正确。");
            return returnData;
        }

        if (zFile.isEncrypted()) {
            zFile.setPassword(passwd.toCharArray());
        }
        try {
            zFile.extractAll(dest);
        }catch (Exception e){
            returnData.put("errorInfo","考试计划包密码错误！");
        }

        //压缩文件注释信息
        returnData.put("annotation",zFile.getComment());
        List<FileHeader> headerList = zFile.getFileHeaders();
        List<File> extractedFileList = new ArrayList<File>();
        for(FileHeader fileHeader : headerList) {
            if (!fileHeader.isDirectory()) {
                extractedFileList.add(new File(destDir,fileHeader.getFileName()));
            }
        }
        File [] extractedFiles = new File[extractedFileList.size()];
        extractedFileList.toArray(extractedFiles);
        return returnData;
    }

    /**
     * 压缩指定文件到当前文件夹
     * @param src 要压缩的指定文件
     * @return 最终的压缩文件存放的绝对路径,如果为null则说明压缩失败.
     */
    public static String zip(String src) {
        return zip(src,null,null,null,null);
    }

    /**
     * 使用给定密码压缩指定文件或文件夹到当前目录
     * @param src 要压缩的文件
     * @param passwd 压缩使用的密码
     * @param extendName 压缩文件扩展名
     * @param fileName 压缩文件名
     * @param content 压缩文件注释
     * @return 最终的压缩文件存放的绝对路径,如果为null则说明压缩失败.
     */
    public static String zip(String src, String passwd,String extendName,String fileName,String content) {
        return zip(src, null, passwd,extendName,fileName,content);
    }

    /**
     * 使用给定密码压缩指定文件或文件夹到当前目录
     * @param src 要压缩的文件
     * @param dest 压缩文件存放路径
     * @param passwd 压缩使用的密码
     * @param extendName 压缩文件扩展名
     * @param fileName 压缩文件名
     * @param content 压缩文件注释
     * @return 最终的压缩文件存放的绝对路径,如果为null则说明压缩失败.
     */
    public static String zip(String src, String dest, String passwd,String extendName,String fileName,String content) {
        return zip(src, dest, false, passwd,extendName,fileName,content);
    }

    /**
     * 使用给定密码压缩指定文件或文件夹到指定位置.
     * <p>
     * dest可传最终压缩文件存放的绝对路径,也可以传存放目录,也可以传null或者"".<br />
     * 如果传null或者""则将压缩文件存放在当前目录,即跟源文件同目录,压缩文件名取源文件名,以.zip为后缀;<br />
     * 如果以路径分隔符(File.separator)结尾,则视为目录,压缩文件名取源文件名,以.zip为后缀,否则视为文件名.
     * @param src 要压缩的文件或文件夹路径
     * @param dest 压缩文件存放路径
     * @param isCreateDir 是否在压缩文件里创建目录,仅在压缩文件为目录时有效.<br />
     * 如果为false,将直接压缩目录下文件到压缩文件.
     * @param passwd 压缩使用的密码
     * @param extendName 压缩文件扩展名
     * @param fileName 压缩文件名
     * @param content 压缩包文件注释
     * @return 最终的压缩文件存放的绝对路径,如果为null则说明压缩失败.
     */
    public static String zip(String src, String dest, boolean isCreateDir, String passwd,String extendName,String fileName,String content) {
        File srcFile = new File(src);
        dest = buildDestinationZipFilePath(srcFile, dest,extendName,fileName);
        ZipParameters parameters = new ZipParameters();
        // 压缩方式
        parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
        // 压缩级别
        parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
        if (!StringUtils.isEmpty(passwd)) {
            parameters.setEncryptFiles(true);
            // 加密方式
            parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_STANDARD);
            parameters.setPassword(passwd.toCharArray());
        }
        try {
            ZipFile zipFile = new ZipFile(dest);

            //zipFile.setFileNameCharset("GBK");
            if (srcFile.isDirectory()) {
                // 如果不创建目录的话,将直接把给定目录下的文件压缩到压缩文件,即没有目录结构
                if (!isCreateDir) {
                    File [] subFiles = srcFile.listFiles();
                    for (File file:subFiles) {
                        if(file.isDirectory() && file.listFiles() != null && Array.getLength(file.listFiles()) > 0){
                            for (File file1:file.listFiles()) {
                                zipFile.addFolder(file1.getParent(),parameters);
                                break;
                            }
                        }else{
                            zipFile.addFile(file,parameters);
                        }
                    }
                }else{
                    zipFile.addFolder(srcFile, parameters);
                }
            } else {
                zipFile.addFile(srcFile, parameters);
            }
            if(content != null){
                zipFile.setComment(content);
            }
            return dest;
        } catch (ZipException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 构建压缩文件存放路径,如果不存在将会创建
     * 传入的可能是文件名或者目录,也可能不传,此方法用以转换最终压缩文件的存放路径
     * @param srcFile 源文件
     * @param destParam 压缩目标路径
     * @param extendName 压缩文件扩展名
     * @param fileName 文件名
     * @return 正确的压缩文件存放路径
     */
    private static String buildDestinationZipFilePath(File srcFile,String destParam,String extendName,String fileName) {
        if(extendName == null || ("").equals(extendName)){
            extendName = ".zip";
        }
        if (StringUtils.isEmpty(destParam)) {
            if (srcFile.isDirectory()) {
                if(fileName == null || ("").equals(fileName)){
                    fileName = "/" + srcFile.getName();
                }else{
                    fileName = "/" + fileName;
                }
                destParam = srcFile.getParent() + fileName + extendName;
            } else {
                if(fileName == null || ("").equals(fileName)){
                    fileName = srcFile.getName().substring(0, srcFile.getName().lastIndexOf("."));
                }
                destParam = srcFile.getParent() + "/" + fileName + extendName;
            }
        } else {
            createDestDirectoryIfNecessary(destParam);  // 在指定路径不存在的情况下将其创建出来
            if (destParam.endsWith("/")) {
                if (srcFile.isDirectory()) {
                    if(fileName == null || ("").equals(fileName)){
                        fileName = srcFile.getName();
                    }
                } else {
                    if(fileName == null || ("").equals(fileName)){
                        fileName = srcFile.getName().substring(0, srcFile.getName().lastIndexOf("."));
                    }
                }
                destParam += fileName + extendName;
            }
        }
        return destParam;
    }

    /**
     * 结果包导出
     * @param zipFilePath
     * @param addResultPathList
     * @param resultPath 压缩前结果存放路径
     * @param addZipFileName
     * @return
     * @throws IOException
     */
    public static Boolean addFileToZipPackage(String zipFilePath,List<String> addResultPathList,String resultPath,String addZipFileName) throws IOException {
        //新建一个文件夹 用来存放要压缩的文件
        //判断该文件夹是否存在 不存在就创建  存在就删除在创建
        File zipFile = new File(zipFilePath);
        if(!zipFile.exists()){
            zipFile.mkdirs();
        }else{
            zipFile.delete();
            zipFile.mkdirs();
        }

        //将作答结果文件夹下所有作答包复制到该文件夹下
        for (String addResultPath:addResultPathList) {
            String resultFilePath = zipFilePath + addResultPath.substring(addResultPath.lastIndexOf("/"), addResultPath.length());
            File resultFile = new File(resultFilePath);
            if(!resultFile.exists()){
                resultFile.createNewFile();
            }
            FileUtil.copyFile(addResultPath,resultFilePath);
        }

        //将日志文件夹整个文件夹copy到该文件夹下的logs
        //String logFolderPath = zipFilePath + addLogsPath;
        //String oldLogPath = ExamSitePath.rootPath + "/files/logs";
        //File logFile = new File(logFolderPath);
        //if(!logFile.exists()){
        //    logFile.mkdirs();
        //}
        //将原有的日志文件夹全部内容复制到新日志文件夹中
        //FileUtil.copyFolder(oldLogPath,logFolderPath);

        //压缩
        String zip = zip(zipFilePath,resultPath,null,null,null,null);
        if(zip != null){
            FileUtil.deleteDirectory(zipFilePath);
        }
        return true;
    }

    /**
     * 在必要的情况下创建压缩文件存放目录,比如指定的存放路径并没有被创建
     * @param destParam 指定的存放路径,有可能该路径并没有被创建
     */
    private static void createDestDirectoryIfNecessary(String destParam) {
        File destDir = null;
        if (destParam.endsWith("/")) {
            destDir = new File(destParam);
        } else {
            destDir = new File(destParam.substring(0, destParam.lastIndexOf("/")));
        }
        if (!destDir.exists()) {
            destDir.mkdirs();
        }
    }

    public static void main(String[] args) throws Exception {
       //unzip("C:/testUpload/test123.zip","C:/testUpload","123");
    }
}
